{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PYNQ Pattern Generator LED string example\n",
    "\n",
    "* First version: 16 March 2021\n",
    "\n",
    "* Updated: 9 April 2021 (Hackathon release)\n",
    "\n",
    "Author: CMC\n",
    "\n",
    "## Requirements\n",
    "\n",
    "PYNQ v2.6 with custom changes to pattern_generator.py. Updated pattern_generator.py should be included with this notebook and must be copied to the board to overwrite the existing pattern_generator.py before running. \n",
    "\n",
    "## Introduction\n",
    "\n",
    "* This notebook shows how to use the PYNQ logictools *Pattern Generator* to  control multi-color LED strings. \n",
    "\n",
    "The notebook covers:\n",
    "\n",
    "* Background information on LED strings\n",
    "* Information about timing specification and timing for the most popular type of LED strings\n",
    "* Instructions to connect a Grove LED string to the PYNQ-Z2 board\n",
    "* Python code to use the logictools Pattern Generator to generate patterns to control the LEDs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Table of Contents\n",
    "\n",
    "* [Background](#Background)\n",
    "* [Grove LED stick](#Grove-LED-stick)\n",
    "* [Board set up](#Board-set-up)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Background \n",
    "\n",
    "Low-cost RGB LED *strings* are available in many formats and can be used for a variety of interesting projects. The LEDs are available in different formats. You can get individual LEDs, strings, rings, and panels or arrays. \n",
    "\n",
    "https://www.adafruit.com/category/168\n",
    "\n",
    "Individual LEDs can be wired together, and you can use a string of LEDs which are flexible, so you can share them to a surface or object to make interesting displays. \n",
    "\n",
    "Neopixels is a popular brand name for these LED strings. \"WS2812\" or \"WS2813\" is the technical name for the  controller or protocol used to control these LEDs.  \n",
    "\n",
    "## Grove LED stick\n",
    "\n",
    "The Grove RGB LED stick has 10 WS2813 LEDs: \n",
    "https://wiki.seeedstudio.com/Grove-RGB_LED_Stick-10-WS2813_Mini/\n",
    "\n",
    "We will use a PYNQ enabled board to build a controller for the LED stick. \n",
    "\n",
    "Ref: https://en.wikipedia.org/wiki/LED_strip_light\n",
    "\n",
    "<div class=\"alert alert-box alert-info\">\n",
    "   \n",
    "Note: WS2813 is a newer version of the WS2813. This notebook can be used with either type of LED.</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Controller Protocol\n",
    "\n",
    "Each LED has a separate Red, Green and Blue part. These three parts can be turned on or off independently to create different colors. Pixels in most TVs, computer monitors or phone screens work in this way. The LEDs are bigger than a \"pixel\" and can be much brighter, allowing you to make different types of displays. \n",
    "\n",
    "The LEDs work by writing a value to each color component. Each color value is 8-bits (or eight 1's or 0's). The value for each color can be from 0 (all zeros, or fully \"off\") to 255 (all ones or fully \"on\"). Combining all three 8-bit values gives us a 24-bit value for one LED. This allows us to generate over 16 Million colors for each LED. \n",
    "\n",
    "If you only have one LED, you only need to write one 24-bit value - 24 ones or zeros. If you have a string of LEDs, you can send one 24-bit LED value after another. The stream of data will be passed down the chain and a value will be assigned to each LED. \n",
    "\n",
    "The LED controller uses a serial protocol to send values to the LEDs. This means that the controller only uses 1 wire to send values to the LEDs. The controller can only write a ONE or a ZERO on this wire to send information. \n",
    "The serial protocol also means that you can connect long chains of LEDs together. In theory, there is no upper limit on the number of LEDs you can connect."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Timing\n",
    "\n",
    "The color values we want to send will be transmitted by setting the value of the signal we write to 1 for some amount of time, and then 0 for another specific amount of time. By varying the time that the signal is 1 and 0 will determine if a value of 1 or 0 is sent. The LED controller will decode the signal and use the value we send to turn on the LED. \n",
    "\n",
    "For the WS2812, to send a ZERO, we will set the signal high for at least 200 nanoseconds and not more than 500 nanoseconds, and low for at least 450 nanoseconds (and not more than 5500 nanoseconds).\n",
    "\n",
    "To send a ONE we will set the signal high for at least 550 nanoseconds and low for at least 450 nanoseconds. \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These are the timings we need:\n",
    "\n",
    "\n",
    "|<td colspan=1>Simplified timing WS2812   |\n",
    "| --- | ----- | --- | --- | --- | --- | ---\n",
    "|Symbol\t| Parameter\t            | Min\t| Typical\t| Max\t| Units | PG pulses @ 10MHz | PG pulses @ 5MHz | \n",
    "|T0H\t    | 0 code high voltage time\t| 200\t| 350\t    | 500\t| ns | 3 | 1\n",
    "|T1H\t    | 1 code high voltage time\t| 550\t| 700\t    | 5,500\t| ns | 6 | 3 \n",
    "|T0L\tdata | low voltage time\t    | 450\t| 600\t    | 5,000\t| ns | 5 | 3 (this may not meet spec)\n",
    "|T1L\tdata | low voltage time\t    | 450\t| 600\t    | 5,000\t| ns | 5 | 3 (this may not meet spec)\n",
    "|TLL\tlatch|  low voltage time\t| 6,000\t| \t\t    |       | ns | 60 | 30\n",
    "\n",
    "If we hold low for >6000 ns, it will rest (ready to send next pattern to LED string)\n",
    "Ref: https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/\n",
    "https://www.arrow.com/en/research-and-events/articles/protocol-for-the-ws2812b-programmable-led\n",
    "\n",
    "|<td colspan=1>Simplified timing WS2812   |\n",
    "| --- | ----- | --- | --- | --- | --- | ---\n",
    "|Symbol\t| Parameter\t            | Min\t| Max\t| Units | PG pulses @ 10MHz | PG pulses @ 5MHz | \n",
    "|T0H\t    | 0 code high voltage time\t| 220\t| 380\t  | ns | 3 | ~1 (this may not meet spec)\n",
    "|T1H\t    | 1 code high voltage time\t| 580\t| 1600\t  | ns | 6 | 3 \n",
    "|T0L\tdata | low voltage time\t    | 580\t| 1600\t    |  ns | 6 | 2 \n",
    "|T1L\tdata | low voltage time\t    | 220\t| 420\t    |  ns | 3 | 2 \n",
    "|TLL\tlatch|  low voltage time\t| 280\t| \t\t    | us | 28000 | 14000\n",
    "\n",
    "Ref: https://files.seeedstudio.com/wiki/Grove-RGB_LED_Stick-10-WS2813_Mini/res/WS2813-Mini.pdf\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The PYNQ-Z2 can be used to build a controller that will send patterns at these very precise times. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Board set up\n",
    "\n",
    "The following code is intended to be run on the PYNQ-Z2 board using the *logictools* overlay. Logictools includes a programmable pattern generator. This allows us to define patterns and write them out on the pins on the board. \n",
    "\n",
    "Any WS2812 string should work with this notebook. The pattern generator can run at different speeds. The speed or the frequency will determine the time a \"sample\" or value is held high or low. Based on this measurement, we can figure out how many 1s and 0s we need to generate the timed pattern for the LEDs.\n",
    "\n",
    "### Connections:\n",
    "The controller has one data wire, but all electrical systems need power and ground, so we will have 3 wires altogether. \n",
    "The WS2813 included in the PYNQ hackathon kit uses a Grove connector. This connector has 4 wires: Power, ground, data (our control wire) and the fourth wire is unconnected. The reason we have an unconnected wire is because the Grove connector is a standard connecter. It is convenient to use it so that it can easily connect to different things even though we don't use all four wires. \n",
    "\n",
    "* Connect the Base shield to the board. \n",
    "* Connect the Grove wire to the WS2813 LED string and to the UART port of the Base Shield. \n",
    "* Output pin D0 from the PYNQ-Z2 board is connected to the \"Rx\" pin of the shield, and therefore to our LED string through the Grove connector. \n",
    "* The power and ground wires will be connected via the shield. \n",
    "\n",
    "![](./images/IMG_2204.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Define bit patterns for 'ones' and 'zeros'\n",
    "\n",
    "Based on the timing values for logic tools, we will define the following values for the number of \"high\" and \"low\" pulses we need so that we can generate patterns for different LED values. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "WS2812 = False \n",
    "\n",
    "if(WS2812 is True):\n",
    "    T0H_PULSES = 3\n",
    "    T1H_PULSES = 6\n",
    "    T0L_PULSES = 5\n",
    "    T1L_PULSES = 5\n",
    "    TLL_PULSES = 60 # Not needed unless trying to send multiple sequential patterns quickly. \n",
    "else: # WS2813; \n",
    "    T0H_PULSES = 3\n",
    "    T1H_PULSES = 8\n",
    "    T0L_PULSES = 8 \n",
    "    T1L_PULSES = 4\n",
    "    TLL_PULSES = 28000 # Not needed unless trying to send multiple sequential patterns quickly. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-box alert-info\"> \n",
    "Note: The timing values inferred from the specification for the WS2813 were found to be unreliable giving incorrect color values. Adding extra samples to the values seems to make the LEDs more stable. \n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Instead of '1's and '0's, *Logictools* uses patterns of 'h' and 'l' for high and low values. We will build a pattern now for a '1' and a '0' "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "t0h = \"h\" * T0H_PULSES\n",
    "t1h = \"h\" * T1H_PULSES\n",
    "t0l = \"l\" * T0L_PULSES\n",
    "t1l = \"l\" * T1L_PULSES\n",
    "tll = \"l\" * TLL_PULSES\n",
    "\n",
    "bit0 = t0h + t0l\n",
    "bit1 = t1h + t1l"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look at these patterns we have just created:    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "print(\"bit0: \" + bit0)\n",
    "print(\"bit1: \" + bit1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see bit0 has 3 'h' followed by 5 'l'. The bit1 has 5 'h' and 5 'l'. \n",
    "Go back and check the timing for these patterns matches what the LED controller requires. \n",
    "\n",
    "We can also check the signals visually by drawing their waveforms. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from pynq.lib.logictools.waveform import draw_wavedrom\n",
    "\n",
    "pattern = {'signal': [{'name': 'clk', 'wave': 'hl' * 8},\n",
    "                      {'name': 'bit0', 'wave': bit0},\n",
    "                      {'name': 'bit1', 'wave': bit1}],\n",
    "           'foot': {'tock': 1},\n",
    "           'head': {'text': 'Pattern'}}\n",
    "\n",
    "draw_wavedrom(pattern)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Write some patterns to the LEDs. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now test out some patterns on the board. First we have to load the *logictools* overlay to the board and create an instance of the pattern generator. We can use this to send values to the board. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "run_control": {
     "frozen": false,
     "read_only": false
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from pynq.overlays.logictools import LogicToolsOverlay\n",
    "logictools_olay = LogicToolsOverlay('logictools.bit')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "* Instantiate the pattern generator "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "run_control": {
     "frozen": false,
     "read_only": false
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "pattern_generator = logictools_olay.pattern_generator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup full LED pattern\n",
    "We created *bit0* and *bit1*. These are single bit values that we can reuse to build up a full 24-bit LED value. \n",
    "\n",
    "We could set all 24 bits to \"1\" e.g. 0xffffff or 0b11111111111111111111. We could do this by using bit1*24. \n",
    "However, this will turn each of the red, green, and blue LEDs to their maximum value which will be very bright. Instead, set the upper 4 bits of each color component to 0, and the lower 4 bits to 1. We will make a variable for this called 'half_on'. We will set up a red, green and blue variable with this value, and then join them together to create a 24-bit. Note the order of the colors is green, then red, then blue. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "half_on = bit0*4 + bit1*4\n",
    "\n",
    "red = half_on\n",
    "green = half_on\n",
    "blue = half_on\n",
    "\n",
    "led_bit_pattern = green+red+blue"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now have a pattern to write a single pixel. We can display the string for this pattern which will be a long sequence of 'h' and 'l' values. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "led_bit_pattern"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we will create the *Wavedrom* formatted dictionary we need to for this pattern."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "led_wavedrom_format = {'signal': [\n",
    "    ['led',\n",
    "        {'name': 'bit0', 'pin': 'D0', 'wave': led_bit_pattern}], \n",
    "    ], \n",
    "    'foot': {'tock': 1},\n",
    "    'head': {'text': 'led_pattern'}}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now use this to set the value of a pixel. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup and run the pattern generator "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "pattern_generator.setup(led_wavedrom_format,\n",
    "                        stimulus_group_name='led', frequency_mhz=10, initial_value=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pattern_generator.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You should now see a LED turn on. \n",
    "\n",
    "Reset is needed before another pattern can be written by pattern generator, so we will run that next. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "pattern_generator.reset()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now start to display other patterns. You can change the value of the green, red and blue components and rerun the cell below multiple times to see different values. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "on = bit1*8\n",
    "off = bit0*8\n",
    "\n",
    "green = on+off+off\n",
    "red = off+on+off\n",
    "blue = off+off+on\n",
    "white = on*3\n",
    "black = off*3 # ALl leds \"off\"\n",
    "\n",
    "led_bit_pattern = green*10 \n",
    "led_wavedrom_format = {'signal': [\n",
    "    ['led',\n",
    "        {'name': 'bit0', 'pin': 'D0', 'wave': led_bit_pattern}], \n",
    "    ], \n",
    "    'foot': {'tock': 1},\n",
    "    'head': {'text': 'led_pattern'}}\n",
    "pattern_generator.setup(led_wavedrom_format,\n",
    "                        stimulus_group_name='led', frequency_mhz=10, initial_value=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "pattern_generator.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "pattern_generator.reset() "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That was a basic test. Now let's do something more interesting. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Define some helper functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from time import sleep\n",
    "def update_leds(led_bit_pattern):\n",
    "    waveform = {'signal': [\n",
    "        ['led',\n",
    "            {'name': 'bit0', 'pin': 'D0', 'wave': led_bit_pattern}], \n",
    "        ], \n",
    "        'foot': {'tock': 1},\n",
    "        'head': {'text': 'waveform'}}\n",
    "    \n",
    "    pattern_generator.setup(waveform,\n",
    "                            stimulus_group_name='led', frequency_mhz=10, initial_value=0)\n",
    "    pattern_generator.run()\n",
    "    pattern_generator.reset() # Leave PG ready for next update"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "def list_to_pattern(led_array):\n",
    "    pattern = ''\n",
    "    for i, _ in enumerate(led_array):\n",
    "        for j in range(23, -1, -1): # walk through 24 bits\n",
    "            #print(j)\n",
    "            bit_test = (led_array[i] >>j) & 1 \n",
    "            # Append a bit0/bit1 pattern\n",
    "            if bit_test is 0:\n",
    "                pattern = pattern+bit0 \n",
    "            else:\n",
    "                pattern = pattern+bit1 \n",
    "    return pattern"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Try writing some patterns\n",
    "\n",
    "First set up an array for the LEDs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "NUMBER_OF_LEDS = 10 # Change to write to longer strings\n",
    "led_array = [0] * NUMBER_OF_LEDS "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate some random values and update the LEDs. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "# Generate some random RGB values \n",
    "for i in range(NUMBER_OF_LEDS):\n",
    "    led_array[i] = random.randint(0,0x0f0f0f) # The maximum random value is limited so LEDs are not too bright\n",
    "led_bit_pattern = list_to_pattern(led_array)\n",
    "update_leds(led_bit_pattern)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Speed up the pattern generator\n",
    "\n",
    "The Wavedrom format is useful for defining and displaying patterns in the notebook. However, it takes more time to process the wavedrom values, so updating the LEDs is slow. This can be speeded up by using the `update_pattern()` method which allows an array of values to be written to the Pattern Generator. This method is an experimental feature in the current PYNQ image. It will not update the Pattern Generator waveforms which will be cleared when this method is called. The Pattern Generator needs to be set up and in the READY state before `update_patter()` can be called. The following code shows how to use the `update_pattern()`.\n",
    "First, we define values for the number of samples. This is similar to what we did for the Wavedrom patterns, except this time we will generate arrays of 1's and 0's. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "WS2812 = False \n",
    "\n",
    "if(WS2812 is True):\n",
    "    T0H_PULSES = 3\n",
    "    T1H_PULSES = 6\n",
    "    T0L_PULSES = 5\n",
    "    T1L_PULSES = 5\n",
    "    TLL_PULSES = 60 # Not needed unless trying to send multiple sequential patterns quickly. \n",
    "else: # WS2813\n",
    "    T0H_PULSES = 3\n",
    "    T1H_PULSES = 8\n",
    "    T0L_PULSES = 8 \n",
    "    T1L_PULSES = 4\n",
    "    \n",
    "t0h = [1] * T0H_PULSES\n",
    "t1h = [1] * T1H_PULSES\n",
    "t0l = [0] * T0L_PULSES\n",
    "t1l = [0] * T1L_PULSES\n",
    "\n",
    "bit0 = t0h + t0l\n",
    "bit1 = t1h + t1l"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next we setup the Pattern Generator, but with a dummy initial pattern.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dummy_pattern = 'l' # This won't be used. We will update the pattern in the next cell\n",
    "waveform = {'signal': [\n",
    "    ['led',\n",
    "        {'name': 'bit0', 'pin': 'D0', 'wave': dummy_pattern}], \n",
    "    ], \n",
    "    'foot': {'tock': 1},\n",
    "    'head': {'text': 'waveform'}}\n",
    "    \n",
    "pattern_generator.setup(waveform,\n",
    "                        stimulus_group_name='led', frequency_mhz=10, initial_value=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Convert a list of numbers to the serial array required by the Pattern Generator\n",
    "def array_to_pattern(array):\n",
    "    pattern = []\n",
    "    for i, _ in enumerate(array):\n",
    "        for j in range(23,-1, -1): # walk through 24 bits\n",
    "            bit_test = array[i] & (1<<j)\n",
    "            # Append a bit0/bit1 pattern\n",
    "            if bit_test is 0:\n",
    "                pattern = pattern+bit0 \n",
    "            else:\n",
    "                pattern = pattern+bit1    \n",
    "    return pattern"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "# Generate some random RGB values \n",
    "for i in range(NUMBER_OF_LEDS):\n",
    "    led_array[i] = random.randint(0,0x0f0f0f) # restrict maximum value so LEDs are not too bright\n",
    "led_pattern= array_to_pattern(led_array)\n",
    "pattern_generator.update_pattern(led_pattern)\n",
    "pattern_generator.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pattern_generator.reset()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Widgets\n",
    "\n",
    "Widgets are components that you can add to a Jupyter notebooks to add interactivity. For example, you can add a textbox, a clickable button, dropdown boxes, slides and more to control behaviour of functions in your notebook. \n",
    "\n",
    "See a [list of widgets](https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20List.html) on the iPython documentation. \n",
    "\n",
    "Reference: https://ipywidgets.readthedocs.io"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import ipywidgets as widgets\n",
    "from IPython.display import display"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Button\n",
    "\n",
    "A button widget can be clicked, in this case \"on\" or \"off\". We will use this to turn on and off all the LEDs. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Setup() PG\n",
    "dummy_pattern = 'l' # This won't be used, we will update the pattern in the next cell\n",
    "waveform = {'signal': [\n",
    "    ['led',\n",
    "        {'name': 'bit0', 'pin': 'D0', 'wave': dummy_pattern}], \n",
    "    ], \n",
    "    'foot': {'tock': 1},\n",
    "    'head': {'text': 'waveform'}}\n",
    "pattern_generator.setup(waveform,\n",
    "                        stimulus_group_name='led', frequency_mhz=10, initial_value=0)\n",
    "\n",
    "# Create the toggle button\n",
    "myToggleButton = widgets.ToggleButtons(\n",
    "    options=['off', 'on']\n",
    ")\n",
    "\n",
    "# This function is called with the button is clicked\n",
    "def toggle_leds(button_value):\n",
    "    # Check the value of the button (value.new)\n",
    "    if(button_value.new is \"off\"):\n",
    "        for i, _ in enumerate(led_array):\n",
    "            led_array[i] = 0 # Turn all LEDs \"off\"     \n",
    "        led_pattern= array_to_pattern(led_array)\n",
    "        pattern_generator.update_pattern(led_pattern)\n",
    "        pattern_generator.run()\n",
    "    else:\n",
    "        for i, _ in enumerate(led_array):\n",
    "            led_array[i] = 0x707070 # Set all LEDs to the same \"on\" value\n",
    "        led_pattern= array_to_pattern(led_array)\n",
    "        pattern_generator.update_pattern(led_pattern)\n",
    "        pattern_generator.run()\n",
    "\n",
    "# The button will be \"off\" by default, so set all the LEDs to off initially\n",
    "for i, _ in enumerate(led_array):\n",
    "    led_array[i] = 0\n",
    "led_pattern= array_to_pattern(led_array)\n",
    "pattern_generator.update_pattern(led_pattern)\n",
    "pattern_generator.run()\n",
    "        \n",
    "# Observe the button and call the toggle_leds function when it is clicked. \n",
    "# Pass the *value* of the button to the function (this will be \"on\" or \"off\")\n",
    "myToggleButton.observe(toggle_leds,'value')\n",
    "\n",
    "# Display the button\n",
    "display(myToggleButton)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pattern_generator.reset()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## color picker \n",
    "\n",
    "The color picker widget allows you to select a color from the color palette, or enter a value for the color. This example will show how to add the color picker widget and use it to set the same color to all the LEDs in the string. \n",
    "\n",
    "Note that brighter color values may look different to the color picker. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Setup() PG\n",
    "dummy_pattern = 'l' # This won't be used, we will update the pattern in the next cell\n",
    "waveform = {'signal': [\n",
    "    ['led',\n",
    "        {'name': 'bit0', 'pin': 'D0', 'wave': dummy_pattern}], \n",
    "    ], \n",
    "    'foot': {'tock': 1},\n",
    "    'head': {'text': 'waveform'}}\n",
    "pattern_generator.setup(waveform,\n",
    "                        stimulus_group_name='led', frequency_mhz=10, initial_value=0)\n",
    "\n",
    "# Create the toggle button\n",
    "myToggleButton = widgets.ToggleButtons(\n",
    "    options=['off', 'on']\n",
    ")\n",
    "\n",
    "# Create the colorPicker object\n",
    "myColorPicker = widgets.ColorPicker(\n",
    "    description='Pick a color',\n",
    "    value='black' # Set an initial value (black is off)\n",
    ")\n",
    "\n",
    "def write_color(color):\n",
    "    # Color value is a hex string with a leading '#' character, and is Red, Green, Blue (RGB) format \n",
    "    # LEDs need Green, Red, Blue (GRB)\n",
    "    \n",
    "    RGB = int(color.new[1:], 16) # remove '#' and convert string to number\n",
    "    \n",
    "    #Split into red, green, blue components\n",
    "    red = (RGB >> 16) & 0xff\n",
    "    green = (RGB >> 8) & 0xff\n",
    "    blue = RGB & 0xff\n",
    "    \n",
    "    # Convert to GRB format\n",
    "    GRB = (green << 16) + (red << 8) + blue\n",
    "    \n",
    "    # Update array. All pixels will get the same value\n",
    "    for i, _ in enumerate(led_array):\n",
    "        led_array[i] = GRB\n",
    "         \n",
    "    led_pattern= array_to_pattern(led_array)\n",
    "    pattern_generator.update_pattern(led_pattern)\n",
    "    pattern_generator.run()\n",
    "\n",
    "    \n",
    "# The default value is black, or \"off\", so set all the LEDs to off initially\n",
    "for i in (led_array):\n",
    "    led_array[i] = 0           \n",
    "     \n",
    "led_pattern= array_to_pattern(led_array)\n",
    "pattern_generator.update_pattern(led_pattern)\n",
    "pattern_generator.run()\n",
    "\n",
    "\n",
    "# Observe the button and call the toggle_leds function when it is clicked. \n",
    "# Pass the *value* of the colorPicker to the function (this will be a string e.g. black: #000000)\n",
    "myColorPicker.observe(write_color,'value')\n",
    "\n",
    "# Display the colorPicker\n",
    "display(myColorPicker)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pattern_generator.reset()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Next steps: Build your own project\n",
    "\n",
    "This notebook shows how to use the LogicTools *Pattern Generator* to create patterns that can be used to control external peripherals. In this design an LED controller was created. \n",
    "You also saw how to use iPython Widgets to add interactive elements to your notebook. \n",
    "\n",
    "You can extend this notebook to build more elaborate LED displays, or add more widgets or functionality to the functions the existing widgets control. The buttons and switches on the board can also be used to control your LED. See the Buttons and Switches example on the board in the directory: base/board/board_btns_leds.ipynb.\n",
    "\n",
    "For example, create functions to\n",
    "* Flash the LEDs\n",
    "* Increase or decrease the speed the LEDs flash\n",
    "* Build an LED counter\n",
    "* Display some interesting patterns\n",
    "* Cycle through each color value for each LED\n",
    "\n",
    "Extend the widget examples\n",
    "* Add a button to select individual LEDs, and then use the color picker to set the color for each LED. \n",
    "* Add a new dropdown box widget to select colors for LEDs. \n",
    "\n",
    "\n",
    "Build some applications\n",
    "* Build a traffic light. Use an LED for the STOP, GO, and AMBER light. Use another LED for a pedestrian crossing. Use widgets to press a button for the pedestrian crossing to change the light sequence. \n",
    "* Build a 1 dimension (1D) game on your LED string. e.g. https://www.instructables.com/Make-Your-Own-1D-Pong-Game/. Can you come up with your own game?\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Copyright (C) 2021 Xilinx, Inc\n",
    "\n",
    "SPDX-License-Identifier: BSD-3-Clause"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "----"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  },
  "nbpresent": {
   "slides": {
    "07899ba6-25e4-4425-8a83-ced90a84d430": {
     "id": "07899ba6-25e4-4425-8a83-ced90a84d430",
     "prev": "58ba9a60-18b5-4988-8fc9-9e5e0a761a44",
     "regions": {
      "afb41902-0dfd-4f57-b9d1-765ff7cd38e2": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "c65c7ff7-8bea-4021-b29b-1febf14436dd",
        "part": "whole"
       },
       "id": "afb41902-0dfd-4f57-b9d1-765ff7cd38e2"
      }
     }
    },
    "08366e17-c8fb-4e81-ac4f-acb3ffdbc5db": {
     "id": "08366e17-c8fb-4e81-ac4f-acb3ffdbc5db",
     "prev": "cf589142-353f-4315-ace7-d5aa2c5cd3dd",
     "regions": {
      "6782802f-f10a-432c-8dc0-24515e875d40": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "c72b3736-696f-4377-ae44-2ffc5590beea",
        "part": "whole"
       },
       "id": "6782802f-f10a-432c-8dc0-24515e875d40"
      }
     }
    },
    "0c7aa757-342b-467c-8897-da18edc26325": {
     "id": "0c7aa757-342b-467c-8897-da18edc26325",
     "prev": "ac1c491b-978f-46b2-a804-8b226ce12445",
     "regions": {
      "aeea45fa-b289-4b19-b470-46c2412978d5": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "8d7dc312-27a9-490d-abfb-eff827a0669d",
        "part": "whole"
       },
       "id": "aeea45fa-b289-4b19-b470-46c2412978d5"
      }
     }
    },
    "1c8fbe49-cf1a-4c13-9149-87396f5d2d43": {
     "id": "1c8fbe49-cf1a-4c13-9149-87396f5d2d43",
     "prev": "c66f3737-7106-42fa-8299-56d309c7f58b",
     "regions": {
      "0838d610-e6b7-4677-84b2-4d75fa82616c": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "108dbebc-aa60-4e16-bed7-a49fc963326b",
        "part": "whole"
       },
       "id": "0838d610-e6b7-4677-84b2-4d75fa82616c"
      }
     }
    },
    "2308a269-bda0-4dcb-8f56-08cd8515481c": {
     "id": "2308a269-bda0-4dcb-8f56-08cd8515481c",
     "prev": "24734f0c-f62b-4d80-8bb1-b72807a1db62",
     "regions": {
      "2cbe2352-6041-49a6-8915-3b09d84c7eec": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "9aeda803-b81c-4005-8a90-27501e431290",
        "part": "whole"
       },
       "id": "2cbe2352-6041-49a6-8915-3b09d84c7eec"
      }
     }
    },
    "24734f0c-f62b-4d80-8bb1-b72807a1db62": {
     "id": "24734f0c-f62b-4d80-8bb1-b72807a1db62",
     "prev": "6148ba10-d53e-4f7f-b114-9a47a9c04809",
     "regions": {
      "f0b4c308-9774-4c45-96d0-35391057d581": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "c87d8ffb-edc1-4f74-a148-85379fe95e95",
        "part": "whole"
       },
       "id": "f0b4c308-9774-4c45-96d0-35391057d581"
      }
     }
    },
    "272b26e0-ad15-4290-a72d-c0ebc37ac87c": {
     "id": "272b26e0-ad15-4290-a72d-c0ebc37ac87c",
     "prev": "2308a269-bda0-4dcb-8f56-08cd8515481c",
     "regions": {
      "b58b2653-d5c5-42a7-bb5f-24175a6cb77f": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "b283db1c-e40a-41f1-8c0d-5750db13ba82",
        "part": "whole"
       },
       "id": "b58b2653-d5c5-42a7-bb5f-24175a6cb77f"
      }
     }
    },
    "3feaf1fb-eeb4-433c-9dbf-3c1775a252db": {
     "id": "3feaf1fb-eeb4-433c-9dbf-3c1775a252db",
     "prev": "eebe3b35-f197-4cd1-885f-4536bd79fc1e",
     "regions": {
      "b7c4f415-8d03-4670-9539-fb11144b11ee": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "dadb569b-c20d-48e0-9c91-0614867568c3",
        "part": "whole"
       },
       "id": "b7c4f415-8d03-4670-9539-fb11144b11ee"
      }
     }
    },
    "4b01980b-925d-4d71-9da2-bce29afd2368": {
     "id": "4b01980b-925d-4d71-9da2-bce29afd2368",
     "prev": "d6180808-ff71-4606-960f-ea33915d76fc",
     "regions": {
      "6e04c87a-fa70-4958-b959-aaf94f94913c": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "333c5227-9c43-4b1b-824b-3e4ddd188651",
        "part": "whole"
       },
       "id": "6e04c87a-fa70-4958-b959-aaf94f94913c"
      }
     }
    },
    "58ba9a60-18b5-4988-8fc9-9e5e0a761a44": {
     "id": "58ba9a60-18b5-4988-8fc9-9e5e0a761a44",
     "prev": "272b26e0-ad15-4290-a72d-c0ebc37ac87c",
     "regions": {
      "d530c786-af70-4107-944d-d1cdbf5ea9da": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "0edabdd6-d23b-4a01-a709-5fe95575eb11",
        "part": "whole"
       },
       "id": "d530c786-af70-4107-944d-d1cdbf5ea9da"
      }
     }
    },
    "6148ba10-d53e-4f7f-b114-9a47a9c04809": {
     "id": "6148ba10-d53e-4f7f-b114-9a47a9c04809",
     "prev": "aac4e37d-47e7-4807-b784-80de55d37f98",
     "regions": {
      "578a3f8a-0c1f-4980-b25c-10889ffa974c": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "4900f181-bc06-478e-8194-939ef978bd8d",
        "part": "whole"
       },
       "id": "578a3f8a-0c1f-4980-b25c-10889ffa974c"
      }
     }
    },
    "706271b3-dc1d-4004-8bda-8f6c8a9f71cb": {
     "id": "706271b3-dc1d-4004-8bda-8f6c8a9f71cb",
     "prev": "d2dac60f-d9f1-4455-9b2f-23f93393f241",
     "regions": {
      "26c27058-279d-40a1-94c1-3f1c3ca78882": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "e83af67a-0650-40ca-b625-ae99a36a77e4",
        "part": "whole"
       },
       "id": "26c27058-279d-40a1-94c1-3f1c3ca78882"
      }
     }
    },
    "a63b435f-5349-46b8-928f-efc8f22a3e4f": {
     "id": "a63b435f-5349-46b8-928f-efc8f22a3e4f",
     "prev": null,
     "regions": {
      "eb482349-2787-49a2-9d04-49a429cb8f7c": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "9416c43b-556a-492e-a242-ac249ab5333a",
        "part": "whole"
       },
       "id": "eb482349-2787-49a2-9d04-49a429cb8f7c"
      }
     }
    },
    "a8f25ad5-2754-4843-aff0-773bbe089675": {
     "id": "a8f25ad5-2754-4843-aff0-773bbe089675",
     "prev": "a63b435f-5349-46b8-928f-efc8f22a3e4f",
     "regions": {
      "18c60535-774e-4fca-9bc9-7d7acdfa1219": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "5b7c3ddd-5274-401d-b4ce-c7a64c541d7d",
        "part": "whole"
       },
       "id": "18c60535-774e-4fca-9bc9-7d7acdfa1219"
      }
     }
    },
    "aac4e37d-47e7-4807-b784-80de55d37f98": {
     "id": "aac4e37d-47e7-4807-b784-80de55d37f98",
     "prev": "4b01980b-925d-4d71-9da2-bce29afd2368",
     "regions": {
      "7b9f1541-92e5-43dd-be8a-e61eedc2ff60": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "6103ff40-00f4-4b43-acec-d922586661f0",
        "part": "whole"
       },
       "id": "7b9f1541-92e5-43dd-be8a-e61eedc2ff60"
      }
     }
    },
    "ac1c491b-978f-46b2-a804-8b226ce12445": {
     "id": "ac1c491b-978f-46b2-a804-8b226ce12445",
     "prev": "08366e17-c8fb-4e81-ac4f-acb3ffdbc5db",
     "regions": {
      "996e4cd1-0a0f-45a3-97ae-c7128623f935": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "6810c05d-81ee-46bd-9d25-9defa201c74c",
        "part": "whole"
       },
       "id": "996e4cd1-0a0f-45a3-97ae-c7128623f935"
      }
     }
    },
    "c66f3737-7106-42fa-8299-56d309c7f58b": {
     "id": "c66f3737-7106-42fa-8299-56d309c7f58b",
     "prev": "07899ba6-25e4-4425-8a83-ced90a84d430",
     "regions": {
      "b378af1c-c140-404c-a0c1-bb405f6821ca": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "59231b4b-ee1d-47e6-b8d9-88ab33054553",
        "part": "whole"
       },
       "id": "b378af1c-c140-404c-a0c1-bb405f6821ca"
      }
     }
    },
    "cf589142-353f-4315-ace7-d5aa2c5cd3dd": {
     "id": "cf589142-353f-4315-ace7-d5aa2c5cd3dd",
     "prev": "a8f25ad5-2754-4843-aff0-773bbe089675",
     "regions": {
      "9fe1ee58-4cad-4808-a967-8ca4273a3fbf": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "3b98a228-fa37-4c93-9f1b-3357e2abf13d",
        "part": "whole"
       },
       "id": "9fe1ee58-4cad-4808-a967-8ca4273a3fbf"
      }
     }
    },
    "d2dac60f-d9f1-4455-9b2f-23f93393f241": {
     "id": "d2dac60f-d9f1-4455-9b2f-23f93393f241",
     "prev": "0c7aa757-342b-467c-8897-da18edc26325",
     "regions": {
      "8a6f6d21-f631-40a6-b464-fc8ec22ac744": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "9b5ecf88-9625-41e9-8e4d-b1c9ea1c1460",
        "part": "whole"
       },
       "id": "8a6f6d21-f631-40a6-b464-fc8ec22ac744"
      }
     }
    },
    "d6180808-ff71-4606-960f-ea33915d76fc": {
     "id": "d6180808-ff71-4606-960f-ea33915d76fc",
     "prev": "706271b3-dc1d-4004-8bda-8f6c8a9f71cb",
     "regions": {
      "89b4b565-9e0b-4d77-8cac-d2393bc6816d": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "5824b49d-7db6-4119-a8e0-fbb777008619",
        "part": "whole"
       },
       "id": "89b4b565-9e0b-4d77-8cac-d2393bc6816d"
      }
     }
    },
    "eebe3b35-f197-4cd1-885f-4536bd79fc1e": {
     "id": "eebe3b35-f197-4cd1-885f-4536bd79fc1e",
     "prev": "1c8fbe49-cf1a-4c13-9149-87396f5d2d43",
     "regions": {
      "7ac48262-cb7b-4944-8872-0fa33a910995": {
       "attrs": {
        "height": 0.8,
        "width": 0.8,
        "x": 0.1,
        "y": 0.1
       },
       "content": {
        "cell": "f3543e3c-c32e-4b02-818c-7567b72a098f",
        "part": "whole"
       },
       "id": "7ac48262-cb7b-4944-8872-0fa33a910995"
      }
     }
    }
   },
   "themes": {
    "default": "ca6d09d5-30f4-40f2-84be-550bb4cbd8c9",
    "theme": {
     "ca6d09d5-30f4-40f2-84be-550bb4cbd8c9": {
      "backgrounds": {
       "backgroundColor": {
        "background-color": "backgroundColor",
        "id": "backgroundColor"
       }
      },
      "id": "ca6d09d5-30f4-40f2-84be-550bb4cbd8c9",
      "palette": {
       "backgroundColor": {
        "id": "backgroundColor",
        "rgb": [
         34,
         34,
         34
        ]
       },
       "headingColor": {
        "id": "headingColor",
        "rgb": [
         256,
         256,
         256
        ]
       },
       "linkColor": {
        "id": "linkColor",
        "rgb": [
         66,
         175,
         250
        ]
       },
       "mainColor": {
        "id": "mainColor",
        "rgb": [
         256,
         256,
         256
        ]
       }
      },
      "rules": {
       "a": {
        "color": "linkColor"
       },
       "h1": {
        "color": "headingColor",
        "font-family": "Source Sans Pro",
        "font-size": 5.25
       },
       "h2": {
        "color": "headingColor",
        "font-family": "Source Sans Pro",
        "font-size": 4
       },
       "h3": {
        "color": "headingColor",
        "font-family": "Source Sans Pro",
        "font-size": 3.5
       },
       "h4": {
        "color": "headingColor",
        "font-family": "Source Sans Pro",
        "font-size": 3
       },
       "h5": {
        "color": "headingColor",
        "font-family": "Source Sans Pro"
       },
       "h6": {
        "color": "headingColor",
        "font-family": "Source Sans Pro"
       },
       "h7": {
        "color": "headingColor",
        "font-family": "Source Sans Pro"
       },
       "li": {
        "color": "mainColor",
        "font-family": "Source Sans Pro",
        "font-size": 6
       },
       "p": {
        "color": "mainColor",
        "font-family": "Source Sans Pro",
        "font-size": 6
       }
      },
      "text-base": {
       "color": "mainColor",
       "font-family": "Source Sans Pro",
       "font-size": 6
      }
     },
     "faf24a67-1574-42ab-ab1c-3c53b95bbe05": {
      "backgrounds": {
       "backgroundColor": {
        "background-color": "backgroundColor",
        "id": "backgroundColor"
       }
      },
      "id": "faf24a67-1574-42ab-ab1c-3c53b95bbe05",
      "palette": {
       "backgroundColor": {
        "id": "backgroundColor",
        "rgb": [
         34,
         34,
         34
        ]
       },
       "headingColor": {
        "id": "headingColor",
        "rgb": [
         238,
         238,
         238
        ]
       },
       "linkColor": {
        "id": "linkColor",
        "rgb": [
         170,
         34,
         51
        ]
       },
       "mainColor": {
        "id": "mainColor",
        "rgb": [
         238,
         238,
         238
        ]
       }
      },
      "rules": {
       "a": {
        "color": "linkColor"
       },
       "h1": {
        "color": "headingColor",
        "font-family": "Ubuntu",
        "font-size": 7
       },
       "h2": {
        "color": "headingColor",
        "font-family": "Ubuntu",
        "font-size": 5
       },
       "h3": {
        "color": "headingColor",
        "font-family": "Ubuntu",
        "font-size": 3.75
       },
       "h4": {
        "color": "headingColor",
        "font-family": "Ubuntu",
        "font-size": 3
       },
       "h5": {
        "color": "headingColor",
        "font-family": "Ubuntu"
       },
       "h6": {
        "color": "headingColor",
        "font-family": "Ubuntu"
       },
       "h7": {
        "color": "headingColor",
        "font-family": "Ubuntu"
       },
       "li": {
        "color": "mainColor",
        "font-family": "Ubuntu",
        "font-size": 5
       },
       "p": {
        "color": "mainColor",
        "font-family": "Ubuntu",
        "font-size": 5
       }
      },
      "text-base": {
       "color": "mainColor",
       "font-family": "Ubuntu",
       "font-size": 5
      }
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
